package cn.slipi.common.decorator;

import cn.slipi.common.utils.LogUtils;
import cn.slipi.common.utils.TraceUtil;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.SignalType;
import reactor.core.scheduler.Schedulers;

import java.util.Map;
import java.util.stream.Collectors;

public class PartnerServerHttpRequestDecorator extends ServerHttpRequestDecorator {

    private static final Logger LOGGER = LoggerFactory.getLogger(PartnerServerHttpRequestDecorator.class);

    private final Flux<DataBuffer> body;

    PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) {
        super(delegate);
        final String pathParams = delegate.getPath().toString().equals("/") ? "" : delegate.getPath().toString();
        Map queryParams = delegate.getQueryParams().toSingleValueMap();
        queryParams.put("path_params", pathParams);
        final String queryParamsStr = JSONObject.toJSONString(queryParams);
        //空格符号"            "
        final String headers = delegate.getHeaders().entrySet()
                .stream()
                .map(entry -> "" + entry.getKey() + ":" + String.join(";", entry.getValue()))
                .collect(Collectors.joining(";"));
        Flux<DataBuffer> flux = super.getBody();
        String traceId = TraceUtil.getCurrentTraceId();
        if (this.hasBody(delegate.getMethod())) {
            body = flux.publishOn(Schedulers.single()).map(dataBuffer -> {
                TraceUtil.traceStart(traceId);
                return LogUtils.loggingRequest(LOGGER, headers, dataBuffer);
            }).doFinally(this::end);
        } else {
            body = flux;
            LogUtils.loggingRequest(LOGGER, headers, queryParamsStr);
        }
    }

    private void end(SignalType signalType) {
        TraceUtil.traceEnd();
    }

    private boolean hasBody(HttpMethod method) {
        return HttpMethod.POST == method || HttpMethod.PUT == method || HttpMethod.PATCH == method || HttpMethod.TRACE == method;
    }

    @Override
    public Flux<DataBuffer> getBody() {
        return body;
    }

}